#ifndef SIMPLECONFIGMANAGER_H
#define SIMPLECONFIGMANAGER_H

#include "warlocklib_global.h"

#include <initializer_list>
#include <map>
#include <set>
#include <stdexcept>
#include <string>
#include <utility>
#include <variant>

WARLOCK_NS_BEGIN

template <typename OptionKey, typename... OptionTypes>
class SimpleConfigManager {
public:
  using OptionVariant = std::variant<OptionTypes...>;
  using OptionPair = std::pair<OptionKey, OptionVariant>;

protected:
  std::map<OptionKey, OptionVariant> m_optionMap;
  std::map<OptionKey, OptionVariant> m_defaultMap;

  static bool contains(const std::map<OptionKey, OptionVariant>& map,
    const OptionKey& name) {
    auto i = map.find(name);
    return ! (i == map.end());
  }

  /** A must implement function to convert key type to a string */
  virtual std::string keyToString(const OptionKey& key) const = 0;

public:
  SimpleConfigManager() = default;
  SimpleConfigManager(const SimpleConfigManager& other)
    : m_optionMap(other.m_optionMap)
    , m_defaultMap(other.m_defaultMap) {}
  SimpleConfigManager(std::initializer_list<OptionPair> list) {
    for (const auto& x : list)
      m_defaultMap[x.first] = x.second;
  }

  SimpleConfigManager(const std::map<OptionKey, OptionVariant>& map)
    : m_defaultMap(map) {}

  virtual ~SimpleConfigManager() = default;

  template <typename T>
  T getDefault(const OptionKey& name) const {
    if (! contains(m_defaultMap, name))
      throw std::invalid_argument(
        "Option '" + keyToString(name) + "' not found.");
    return std::get<T>(m_defaultMap.at(name));
  }

  template <typename T>
  T get(const OptionKey& name) const {
    if (contains(m_optionMap, name))
      return std::get<T>(m_optionMap.at(name));
    return getDefault<T>(name);
  }

  template <typename T>
  void set(const OptionKey& name, T& value) {
    m_optionMap[name] = value;
  }

  template <typename T>
  void set(const OptionKey& name, T value) {
    m_optionMap[name] = value;
  }

  /**
   * @brief 添加键值对，不会改变默认设置
   * @param list
   */
  void addOptions(std::initializer_list<OptionPair> list) {
    for (const auto& x : list)
      m_optionMap[x.first] = x.second;
  }

  void addOptions(const SimpleConfigManager& other) {
    for (const auto& x : other.m_optionMap)
      m_optionMap[x.first] = x.second;
  }

  OptionVariant& operator[](const OptionKey& name) {
    if (contains(m_optionMap, name))
      return m_optionMap[name];
    if (contains(m_defaultMap, name)) {
      m_optionMap[name] = m_defaultMap[name];
      return m_optionMap[name];
    }
    return m_optionMap[name] = OptionVariant();
  }

  /**
   * @brief 清空所有数值，值保留默认列表
   *
   * 没有默认值的项目也会删除
   */
  void resetAll() { m_optionMap.clear(); }

  /**
   * @brief 清空默认列表，当前数据不动
   *
   * 如果包含未自定义的参数，将会拷贝到当前列表中
   */
  void clearDefaults() {
    m_optionMap.merge(m_defaultMap);
    m_defaultMap.clear();
  }

  void reset(const OptionKey& name) {
    if (! contains(m_optionMap, name))
      throw std::invalid_argument(
        "Option '" + keyToString(name) + "' not found.");
    if (! contains(m_defaultMap, name))
      throw std::invalid_argument("Option '" + keyToString(name)
                                  + "' doesn't have a default value.");

    m_optionMap.erase(name);
  }

  template <typename T>
  void setDefault(const OptionKey& name, const T& value) {
    m_defaultMap[name] = value;
  }

  void setDefaults(std::initializer_list<OptionPair> list) {
    for (const auto& x : list) {
      m_defaultMap[x.first] = x.second;
    }
  }

  void setDefaults(const SimpleConfigManager& other) {
    for (const auto& x : other.m_defaultMap) {
      m_defaultMap[x.first] = x.second;
    }
  }

  /**
   * @brief 将当前所有状态存入默认设置
   */
  void setCurrentAsDefault() {
    for (const auto& x : m_optionMap) {
      m_defaultMap[x.first] = x.second;
    }
  }

  /**
   * @brief 完全移除某一条目
   *
   * 如果想保留默认设置，请使用 @l{SimpleConfigManager::}{reset()}
   *
   * @param name
   */
  void remove(const OptionKey& name) {
    m_optionMap.erase(name);
    m_defaultMap.erase(name);
  }

  void removeDefault(const OptionKey& name) {
    m_defaultMap.erase(name);
  }

  bool contains(const OptionKey& name) {
    return contains(m_optionMap, name) || contains(m_defaultMap, name);
  }

  bool containsDefault(const OptionKey& name) {
    return contains(m_defaultMap, name);
  }

  SimpleConfigManager& operator<<(OptionPair pair) {
    m_optionMap[pair.first] = pair.second;
    return this;
  }

  SimpleConfigManager& operator>>(const OptionKey& name) {
    remove(name);
  }

  void operator=(const SimpleConfigManager& other) {
    m_defaultMap = other.m_defaultMap;
    m_optionMap = other.m_optionMap;
  }

  /**
   * @brief 判断两个 @e SimpleConfigManager 是否等价
   *
   * 两个 @e SimpleConfigManager 中的数据列表无需完全相等，
   * 只要所有能取到的参数的数值相等，即判断为等价。
   *
   * @note 此判断满足交换律
   * @param other
   * @return
   */
  bool operator==(const SimpleConfigManager& other) const {
    std::set<OptionKey> names, o_names;

    for (auto const& x : m_optionMap) {
      names.insert(x.first);
    }
    for (auto const& x : m_defaultMap) {
      names.insert(x.first);
    }

    for (auto const& x : other.m_optionMap) {
      o_names.insert(x.first);
    }
    for (auto const& x : other.m_defaultMap) {
      o_names.insert(x.first);
    }

    if (names != o_names)
      return false;

    for (const OptionKey& x : names) {
      if ((*this)[x] != other[x])
        return false;
    }
    for (const OptionKey& x : o_names) {
      if (other[x] != (*this)[x])
        return false;
    }
    return true;
  }

  std::set<OptionKey> keys() const {
    std::set<OptionKey> res;
    for (const auto& x : m_defaultMap)
      res.insert(x.first);
    for (const auto& x : m_optionMap)
      res.insert(x.first);
    return res;
  }

  std::set<OptionKey> defaultKeys() const {
    std::set<OptionKey> res;
    for (const auto& x : m_defaultMap)
      res.insert(x.first);
    return res;
  }

  std::set<OptionKey> customedKeys() const {
    std::set<OptionKey> res;
    for (const auto& x : m_optionMap)
      res.insert(x.first);
    return res;
  }

  std::map<OptionKey, OptionVariant> toStdMap() const {
    std::map<OptionKey, OptionVariant> res = m_defaultMap;
    for (const auto& x : m_optionMap)
      res[x.first] = x.second;
    return res;
  }
};

WARLOCK_NS_END

#endif // SIMPLECONFIGMANAGER_H
